package com.sneakxy.mybatis.commons.repository.support;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;
import org.springframework.data.repository.core.support.AbstractEntityInformation;

import com.sneakxy.mybatis.commons.orm.generation.GeneratedValue;
import com.sneakxy.mybatis.commons.utils.MybatisUtils;

/**
 * @author 潜行的青衣
 */
public class MybatisEntityInformation<T, ID extends Serializable> extends AbstractEntityInformation<T, ID> {

	private Field idField;
	
	private Class<ID> idClass;
	
	private GeneratedValue generatedValue;
	
	private boolean persistable;
	
	private boolean composite;
	
	private TypeHandlerRegistry typeHandlerRegistry;
	
	/**
	 * @param domainClass
	 * @param typeHandlerRegistry
	 */
	public MybatisEntityInformation(Class<T> domainClass, TypeHandlerRegistry typeHandlerRegistry) {
		super(domainClass);
		this.typeHandlerRegistry = typeHandlerRegistry;
		this.checkId(typeHandlerRegistry);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean isNew(T entity) {
		if(this.isPersistable()) {
			return ((Persistable<ID>) entity).isNew();
		}
		return super.isNew(entity);
	}

	/* (non-Javadoc)
	 * @see org.springframework.data.repository.core.EntityInformation#getId(java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public ID getId(T entity) {
		ID result = null;
		if(this.isPersistable()) {
			result = ((Persistable<ID>) entity).getId();
		} else {
			try {
				result = (ID) FieldUtils.readField(idField, entity, true);
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		return result;
	}
	
	/* (non-Javadoc)
	 * @see org.springframework.data.repository.core.EntityInformation#getIdType()
	 */
	@Override
	public Class<ID> getIdType() {
		return this.idClass;
	}

	@SuppressWarnings("unchecked")
	private void checkId(TypeHandlerRegistry typeHandlerRegistry) {
		persistable = Persistable.class.isAssignableFrom(this.getJavaType());
		Class<T> clazz = this.getJavaType();
		Field[] fields = FieldUtils.getAllFields(clazz);
		Annotation id = null;
		for(Field field: fields) {
			id = MybatisUtils.getAnnotation(clazz, field, Id.class);
			if(id != null) {
				this.idField = field;
				break;
			}
		}
		if(this.idField == null) {
			throw new IllegalArgumentException("必须有ID");
		}
		if(this.isPersistable()) {
			idClass = (Class<ID>) GenericTypeResolver.resolveTypeArgument(this.getJavaType(), Persistable.class);
		} else {
			idClass = (Class<ID>) idField.getType();
		}
		this.composite = !typeHandlerRegistry.hasTypeHandler(idClass);
		this.generatedValue = MybatisUtils.getAnnotation(this.getJavaType(), idField, GeneratedValue.class);
	}

	public void setId(T entity, ID id) throws IllegalAccessException {
		FieldUtils.writeField(this.idField, entity, id, true);
	}
	
	public GeneratedValue getGeneratedValue() {
		return generatedValue;
	}
	
	public boolean isPersistable() {
		return persistable;
	}

	public boolean isComposite() {
		return composite;
	}

	public TypeHandlerRegistry getTypeHandlerRegistry() {
		return typeHandlerRegistry;
	}
	
}
